09 条件表达式重构

条件表达式重构

如何让条件语句看起来更清晰?

Decompose Conditional(分解条件式)

problem:你有一个复杂的条件(if-then-else)语句。
solution:从if、then、else 三个段落中分别提炼出独立函数。

1
2
3
4
5
6
7
8
9
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else charge = quantity * _summerRate;

//======================after refactoring=========================

if (notSummer(date))
charge = winterCharge(quantity);
else charge = summerCharge (quantity);

重构的原因?

程序之中,【复杂的条件逻辑】是最常导致复杂度上升的地点之一。
对于条件逻辑,【将每个分支条件分解,形成新函数】还可以给你带来更多好处:可以突出条件逻辑,更清楚地表明每个分支的作用,并且突出每个分支的原因。

Consolidate Conditional Expression(合并条件式)

problem:你有一系列条件测试,都得到【相同结果】。
solution:将这些测试合并为一个条件式,并将这个条件式提炼成为一个独立函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
double disabilityAmount() {
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
}

//======================after refactoring=========================

double disabilityAmount() {
if (isNotEligableForDisability()) return 0;
// compute the disability amount
}
boolean isNotEligibleForDisability() {
return ((_seniority < 2) || (_monthsDisabled > 12) || (_isPartTime));
}

重构的原因?

  1. 合并后的条件代码会告诉你【实际上只有一次条件检查,只不过有数个并列条件需要检查而已】,从而使这一次检查的用意更清晰。
  2. 【将检查条件提炼成一个独立函数】对于理解代码意义非常有用,因为它把描述【做什么】的语句换成了【为什么这样做】。

Consolidate Duplicate Conditional Fragments(合并重复的条件片段)

problem:在条件式的每个分支上有着相同的一段代码。
solution:将这段重复代码搬移到条件式之外。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (isSpecialDeal()) {
total = price * 0.95;
send();
}
else {
total = price * 0.98;
send();
}

//======================after refactoring=========================

if (isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();

重构的原因?

代码能更清楚地表明哪些东西随条件的变化而变化、哪些东西保持不变。

Remove Control Flag(移除控制标记)

problem:在一系列布尔表达式(boolean expressions)中,某个变量带有【控制标记】(control flag)的作用。
solution:以break语句或return的语句取代控制标记。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void checkSecurity(String[] people) {
boolean found = false;
for (int i = 0; i < people.length; i++) {
if (! found) {
if (people[i].equals ("Don")){
sendAlert();
found = true;
}
}
}
}

//======================after refactoring=========================

void checkSecurity(String[] people) {
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
sendAlert();
break;
}
}
}

重构的原因?

条件语句真正的用途会清晰得多

Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件式)

problem:函数中的条件逻辑(conditional logic)使人难以看清正常的执行路径。
solution:使用卫语句(guard clauses)表现所有特殊情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 double getPayAmount() {
double result;
if (_isDead) result = deadAmount();
else {
if (_isSeparated) result = separatedAmount();
else {
if (_isRetired) result = retiredAmount();
else result = normalPayAmount();
};
}
return result;
};

//======================after refactoring=========================

double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};

什么是卫语句?

如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。
这样的单独检查常常被称为【卫语句(guard clauses)】

重构的原因?

如果使用if-then-else 结构,你对if 分支和else 分支的重视是同等的。 这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性。卫语句(guard clauses)就不同了,它告诉阅读者:【这种情况很罕见,如果它真的发生了,请做 一些必要的整理工作,然后退出。】

Replace Conditional with Polymorphism(以多态取代条件式)

problem:你手上有个条件式,它根据对象型别的不同而选择不同的行为。
solution:将这个条件式的每个分支放进一个subclass内的覆写函数中,然后将原始函数声明为抽象函数(abstract method)。

该手法在Replace Type Code with State/Strategy中已经使用到。

重构的原因?

如果你需要根据对象的不同型别而采取不同的行为,多态使你不必编写明显的条件式

Introduce Null Object(引入Null对象)

你需要再三检查【某物是否为null value】。
将null value(无效值)替换为null object(无效物)。

重构的原因?

你不必再向对象询问「你是什么型别」 而后根据得到的答案调用对象的某个行为——你只管调用该行为就是了,其他的一切多态机制会为你安排妥当。
Null Object中需要对所有的行为都作出相应的响应,才不再需要作Null的判断。

本文标题:09 条件表达式重构

文章作者:Sun

发布时间:2019年01月14日 - 14:01

最后更新:2019年01月15日 - 20:01

原始链接:https://sunyi720.github.io/2019/01/14/refactoring/09 条件表达式重构/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。